﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ColladaViewer
{
    using Color = System.Drawing.Color;
    using System.Globalization;
    using EffectMaterial = Omi.Xna.Collada.Model.Materials.EffectMaterial;
    using Omi.Xna.Collada.Model.Materials;

    public partial class MaterialsPanel : UserControl
    {
        private List<EffectMaterial> materials;
        private EffectMaterial currentMaterial;
        private string basePath;
        private ModelViewControl modelView;

        public ModelViewControl ModelView
        {
            get { return modelView;}
            set { modelView = value;}
        }

        public MaterialsPanel()        
        {
            InitializeComponent();
            materialListBox.Items.Clear();            
        }

        public void Init(IEnumerable<EffectMaterial> materials, String basePath)
        {
            this.materials = new List<EffectMaterial>(materials);
            this.basePath = basePath;

            // Material Selection List Box
            materialListBox.Items.Clear();
            materialListBox.Items.AddRange(materials.Select(m => 
                (!String.IsNullOrEmpty(m.Name) ? m.Name : "(unnamed)")).ToArray());

            if (materialListBox.Items.Count > 0)
            {
                materialListBox.SelectedIndex = 0;

                ShowMaterialProperties(this.materials[0]);
            }

            materialBox.Text = "Choose Material (" + this.materials.Count + ")";
        }

        void ShowMaterialProperties(EffectMaterial material)
        {
            typeLabel.Text = "Common"; // could be shader in future

            var properties = material.Material.Properties;
            MaterialProperty property;

            // Ambient Button
            property = properties.OfType<AmbientMap>().SingleOrDefault();
            if (property == null)
                property = properties.OfType<AmbientColor>().SingleOrDefault();            

            SetButton(ambientButton, property);

            // Diffuse Button
            property = properties.OfType<DiffuseMap>().SingleOrDefault();
            if (property == null)
                property = properties.OfType<AmbientColor>().SingleOrDefault();

            SetButton(diffuseButton, property);

            // Emissive Button
            property = properties.OfType<EmissiveMap>().SingleOrDefault();
            if (property == null)
                property = properties.OfType<EmissiveColor>().SingleOrDefault();

            SetButton(emissiveButton, property);

            // Specular Button
            property = properties.OfType<SpecularMap>().SingleOrDefault();
            if (property == null)
                property = properties.OfType<SpecularColor>().SingleOrDefault();

            SetButton(specularButton, property);

            // Opacity Button
            property = properties.OfType<OpacityMap>().SingleOrDefault();
            if (property == null)
                property = properties.OfType<Opacity>().SingleOrDefault();

            SetButton(opacityButton, property);

            // Normal Map Button
            property = properties.OfType<NormalMap>().SingleOrDefault();            

            SetButton(bumpButton, property);

            // Specularity Button
            property = properties.OfType<SpecularPower>().SingleOrDefault();
            if (property == null)
                property = properties.OfType<SpecularMap>().SingleOrDefault();

            SetButton(specularityButton, property);

            // Shader-Parameter
            currentMaterial = material;
            shaderParameterSelect.Items.Clear();
            foreach (EffectParameter param in material.Effect.Parameters)
            {
                string name = param.Name;
                if (name == "World" || name == "View" || name == "Projection" ||
                    name == "WorldIT" || name == "EyePosition" ||
                    name.EndsWith("Sampler"))
                    continue;

                shaderParameterSelect.Items.Add(name);
            }
                        
            shaderParameterSelect.Enabled = true;
        }

        void SetButton(Button button, MaterialProperty property)
        {
            if (property == null)
            {
                button.Enabled = false;
                button.Text = "undefined";
                return;
            }
            
            button.Enabled = true;
            button.Image = null;
            button.ForeColor = Color.Black;
            button.Click -= imageButton_Click;
            button.Click -= colorButton_Click;
            button.TextAlign = ContentAlignment.MiddleCenter;

            if (property is TextureProperty)
            {
                Image image;
                TextureReference texture = (property as TextureProperty).Texture;
                string path = texture.Filename;

                if (!Path.IsPathRooted(texture.Filename))
                {                                    
                    path = basePath + "\\" + texture.Filename;                    
                }

                if (File.Exists(path))
                {
                    try
                    {
                        button.Image = Image.FromFile(path);                        
                        button.ImageAlign = ContentAlignment.MiddleCenter;
                        button.ForeColor = Color.White;

                        button.Click += imageButton_Click;
                    } 
                    catch (Exception ex)
                    {
                        Console.WriteLine("Failed to load image '" + path + "'");
                    }                    
                }

                button.TextAlign = ContentAlignment.MiddleRight;
                button.Text = Path.GetFileName(path);
                button.Tag = path;
                button.AccessibleDescription = path;                         
            }
            else if (property is ValueProperty)
            {
                button.Text = (property as ValueProperty).Value + "";
            }
            else if (property is ColorProperty)
            {
                Vector4 values = (property as ColorProperty).Color.ToVector4();
                var color = (property as ColorProperty).Color;

                button.BackColor = Color.FromArgb(color.A, color.R, color.G, color.B);
                button.Text = "";                

                button.Click += colorButton_Click;
            }
            else
            {
                button.Enabled = false;
            }
        }

        private void imageButton_Click(object sender, EventArgs e)
        {                                 
            Button button = sender as Button;
            if (button == null || button.Image == null) return;
        
            PictureBox pic = new PictureBox();
            pic.SizeMode = PictureBoxSizeMode.Zoom;
            pic.Load(button.Tag as string);            

            Form imageDisplay = new Form();
            imageDisplay.Size = new Size(Math.Min(pic.Image.Width, 640), 
                Math.Min(pic.Image.Height, 640)+24);
            imageDisplay.Controls.Add(pic);
            imageDisplay.Text = Path.GetFullPath(button.Text);
            imageDisplay.AutoScroll = true;            

            pic.Dock = DockStyle.Fill;
            
            imageDisplay.ShowDialog();            
        }

        private void colorButton_Click(object sender, EventArgs e)
        {
            Button button = sender as Button;
            if (button == null) return;

            ColorDialog dialog = new ColorDialog();
            dialog.AnyColor = true;
            dialog.AllowFullOpen = true;
            dialog.ShowHelp = true;           
            dialog.CustomColors = new int[] { button.BackColor.ToArgb() };

            dialog.Color = button.BackColor;            

            dialog.ShowDialog();
        }

        private void materialListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ShowMaterialProperties(materials[materialListBox.SelectedIndex]);
        }

        private void shaderParameterSelect_SelectedValueChanged(object sender, EventArgs e)
        {
            shaderParameterValue.Enabled = false;
            shaderParameterButton.Enabled = false;

            string name = shaderParameterSelect.Text;
            if (currentMaterial.Effect.Parameters[name] == null)
                return;

            EffectParameter param = currentMaterial.Effect.Parameters[name];
            EffectParameterType type = param.ParameterType;
            
            if (type == EffectParameterType.Single && param.ColumnCount == 3)
            {
                Vector3 value = param.GetValueVector3();
                shaderParameterValue.Text =
                    value.X + " " + value.Y + " " + value.Z;
            }
            else if (type == EffectParameterType.Single && param.ColumnCount == 1)
            {
                float value = param.GetValueSingle();
                shaderParameterValue.Text = value + "";
            }
            else if (type == EffectParameterType.Texture)
            {
                shaderParameterValue.Text = "";
                return;
            }

            shaderParameterValue.Enabled = true;
            shaderParameterButton.Enabled = true;
        }

        private void shaderParameterButton_Click(object sender, EventArgs e)
        {
            string name = shaderParameterSelect.Text;
            if (currentMaterial.Effect.Parameters[name] == null)
                return;

            EffectParameter param = currentMaterial.Effect.Parameters[name];
            EffectParameterType type = param.ParameterType;

            if (type == EffectParameterType.Single && param.ColumnCount == 3)
            {
                string[] input = shaderParameterValue.Text.Split(' ');
                if (input.Length != 3)
                {
                    MessageBox.Show("3 values required");
                    return;
                }

                Vector3 value = new Vector3();
                value.X = float.Parse(input[0], CultureInfo.CurrentCulture);
                value.Y = float.Parse(input[1], CultureInfo.CurrentCulture);
                value.Z = float.Parse(input[2], CultureInfo.CurrentCulture);

                param.SetValue(value);
            }
            else if (type == EffectParameterType.Single && param.ColumnCount == 1)
            {
                float value;
                value = float.Parse(shaderParameterValue.Text, 
                    CultureInfo.CurrentCulture);                

                param.SetValue(value);
            }
            else if (type == EffectParameterType.Texture)
            {                
                return;
            }

            modelView.Update();
            modelView.Invalidate();
        }

        private void shaderParameterValue_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
                shaderParameterButton_Click(null, null);
        }
    }
}
